package pprz.components;

import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;

import net.jscience.math.MathFP;
import pprz.data_components.Message;
import pprz.data_components.events.AcSelectEvent;
import pprz.data_components.events.AcSelectEventListener;
import pprz.data_components.events.DownlinkMessageEvent;

public class HorizonIndicator extends Indicator 
implements AcSelectEventListener {
	
	protected static final long serialVersionUID = 1L;
	protected int roll;
	protected int pitch;
	protected int ac;
	
	protected int ticklen;
	protected int step;
	protected int ticks;
	protected int r;
	protected FontMetrics fm;
	protected int w;
	protected int h;
	protected int cx; // max space required for text label
	protected int cy;

	protected BufferedImage ticks_and_marks;
	protected Ellipse2D.Float dial;
	
	
	public HorizonIndicator()
	{
		super();
		ac=0;pitch=0;roll=45;
		ticks_and_marks=new BufferedImage(
				1,
				1,
				BufferedImage.TYPE_4BYTE_ABGR
				);
		dial=new Ellipse2D.Float(0,0,1,1);
		setSize(100,100);
		setPreferredSize(new Dimension(100,100));
		validate();
	}

	public void set(int r, int p, int a, int v)
	{ 
		pitch=p; roll=r;
		pitch=(pitch<180)?pitch:pitch-360;
		validate();
		repaint(100);
	}
	
	public void setAC(int ac)
	{
		this.ac=ac;
	}	
	
	@Override
	protected void draw(Graphics g) {
		Graphics2D g2=(Graphics2D)g;
		g2.setComposite(AlphaComposite.Clear);
		g2.setPaint(new Color(0f,0f,0f,0f));
		g2.fillRect(0, 0, getWidth(), getHeight());
		g2.setPaintMode();
		g2.setClip(dial);
		//draw sky area
		g2.setColor(new Color(0x99AAFF));
		g2.fillRect(0, 0, getWidth(), getHeight());
		//draw ground area
		g2.setColor(new Color(0xFFDD00));
		g2.fillArc(cx-cx,cy-cx,2*r+2*cx,2*r+2*cx,(int)roll+180,180);
		//draw red divider
		g2.setStroke(new BasicStroke(1));
		g2.setColor(Color.red);
		g2.drawLine(cx,r+cy,2*r+cx,r+cy);
		//draw pitch indicator
		if(pitch<90&&pitch>-90)
		{
			//for regular flight pitch
			g2.setColor(new Color(0x7788DD));
			g2.fillRect(r-r/5+cx,r/3+cy,r*2/5,r+2*r*pitch/90/3-r/3);
			g2.setColor(new Color(0xCCAA00));
			g2.fillRect(r-r/5+cx,r+2*r*pitch/90/3+cy,r*2/5,r-2*r*pitch/90/3-r/3);
		}
		else
		{
			//for upside-down flight pitch
			pitch=(pitch>90)?pitch-180:pitch+180;
			g2.setColor(new Color(0x666600));
			g2.fillRect(r-r/5+cx,r/3+cy,r*2/5,r+2*r*pitch/90/3-r/3);
			g2.setColor(new Color(0x9999FF));
			g2.fillRect(r-r/5+cx,r+2*r*pitch/90/3+cy,r*2/5,r-2*r*pitch/90/3-r/3);
		}
		g2.setClip(null);
		g.drawImage(ticks_and_marks,0,0,null);
	}

	
	protected void drawLabelsAndDials(Graphics g)
	{
		System.out.println("Horizon::public void draw(Graphics g) called");
		Graphics2D g2=(Graphics2D)g;
		double angle;
		//cosmetics and accents
		g2.setStroke(new BasicStroke(1));
		g2.setColor(Color.black);
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
		g2.draw(dial);
		g2.setStroke(new BasicStroke(1));
		g2.drawRect(r-r/5+cx, r/3+cy, r*2/5, 4*r/3);
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_OFF);
		//draw radial tickmarks&labels
		//right side
		String label;
		for (int i=0; i<ticks/2; i++)
		{
			//draw labels
			angle=i*2*Math.PI/ticks+Math.PI/2.;
			if (i*360/ticks % step == 0)
			{
				label=String.valueOf(i*360/ticks);
				w=fm.stringWidth(label);
				g2.drawString(label, 
					(int)((r+0.7*ticklen)*Math.sin(angle)+r+cx-Math.abs(angle-Math.PI/2)*w/Math.PI),
				    (int)((r+0.7*ticklen)*Math.cos(angle)+r+cy+Math.abs(1-angle/Math.PI)*h));			
			}
			//draw tickmarks
			g2.drawLine((int)(r*Math.sin(angle))+r+cx,
					    (int)(r*Math.cos(angle))+r+cy,
					    (int)((r-ticklen)*Math.sin(angle))+r+cx,
					    (int)((r-ticklen)*Math.cos(angle))+r+cy );
		}
		//left side
		for (int i=0; i<ticks/2; i++)
		{
			angle=i*2*Math.PI/ticks+3*Math.PI/2;
			//draw labels
			if (i*360/ticks % step == 0) 
				{
					label=String.valueOf(i*360/ticks+180);
					w=fm.stringWidth(label);
					g2.drawString(label, 
								  (int)((r+0.7*ticklen)*Math.sin(angle)+r+cx-Math.abs(5*Math.PI/2-angle)*w/Math.PI),
								  (int)((r+0.7*ticklen)*Math.cos(angle)+r+cy+Math.abs(angle-Math.PI)*h/Math.PI));
				}
			//draw tickmarks
			g2.drawLine((int)(r*Math.sin(angle))+r+cx,
					    (int)(r*Math.cos(angle))+r+cy,
					    (int)((r-ticklen)*Math.sin(angle))+r+cx,
					    (int)((r-ticklen)*Math.cos(angle))+r+cy );
		}
		//draw pitch tickmarks&labels
		if(pitch<90&&pitch>-90)
		{
			//for regular flight pitch
			int y_ini=r+cy;
			int n=(r<50)?1:(r<70)?3:(r<100)?6:12;
			int y=y_ini+4*r/3/n;
			int i=-180/n;
			while (y<5*r/3+cy) {
				g2.drawLine(r+r/5+cx+1,y,r+r/5+cx+ticklen,y);
				label=String.valueOf(i);
				w=fm.stringWidth(" ");
				g2.drawString(label,r+r/5+cx+ticklen+w,y+h/2);
				y+=4*r/3/n;
				i-=180/n;				
			}
			y=y_ini; i=0;
			while (y>r/3+cy) {
				g2.drawLine(r+r/5+cx+1,y,r+r/5+cx+ticklen,y);
				label=String.valueOf(i);
				w=fm.stringWidth(" -");
				g2.drawString(label,r+r/5+cx+ticklen+w,y+h/2);
				y-=4*r/3/n;
				i+=180/n;
//				System.out.println("two\n");
			}
		}
		else
		{
			//for upside-down flight pitch
//			pitch=(pitch>90)?pitch-180:pitch+180;
//			g2.setColor(new Color(0x666600));
//			g2.fillRect(r-r/5+cx,r/3+cy,r*2/5,r+2*r*pitch/90/3-r/3);
//			g2.setColor(new Color(0x9999FF));
//			g2.fillRect(r-r/5+cx,r+2*r*pitch/90/3+cy,r*2/5,r-2*r*pitch/90/3-r/3);
		}			
	}
	
	
	
	
	@Override
	public void messageEvent(DownlinkMessageEvent ev) {
		Message m = ev.getMessage();
		//process only downlink messages id=6 from currect AC
		if (m.msgClass()!=0 || m.id()!=6 || m.acRec().get_id()!=ac) return; 
		set((int)MathFP.toLong(m.getFields().get(0)[0]),
			(int)MathFP.toLong(m.getFields().get(2)[0]),0,0);
	}

	public void acSelectEvent(AcSelectEvent ev) {
		this.ac=ev.getAc();
		validate();
		repaint(100);
	}
	
	@Override
	public void setBounds(int x, int y, int width, int height) {
		super.setBounds(x, y, width, height);
		if (width<1 || height<1) return;
		
		calcParameters(width, height);
		ticks_and_marks=new BufferedImage(
				width,
				height,
				BufferedImage.TYPE_4BYTE_ABGR
				);
		dial=new Ellipse2D.Float(cx,cy,2*r,2*r);
		this.drawLabelsAndDials(ticks_and_marks.getGraphics());
	}	
	
	
	protected void calcParameters(int width, int height)
	{
		r=Math.min(width,height)/2;
		ticklen=Math.round(0.05f*r);
		step=(r<50)?90:(r<100)?45:15;
		ticks=360/step;
		fm=buff.getGraphics().getFontMetrics();
		w=fm.stringWidth("180");;
		h=fm.getHeight()-fm.getDescent();
		cx=Math.round(0.1f*r+w); // max space required for text label
		cy=Math.round(0.1f*r+h);
		r=r-Math.max(cx,cy); // circle radius to fit the space and draw the label too
		r=(r<1)?1:r;
	}
	
}
